﻿using System.Linq;
using System.Data.Entity;
using System;
using System.Collections.Generic;
using System.Data.Objects.DataClasses;
using System.Data.EntityClient;
using System.Configuration;
using System.Data.Objects;
using System.Data;

namespace Warehouse.DataModel
{
    public partial class Repository
    {
        Lazy<Unit> _oz;
        Lazy<Unit> _gallon;
        Lazy<Unit> _color;
        Lazy<Unit> _set;
        Lazy<Unit> _kit;
        Lazy<Unit> _noUnit;

        Lazy<Configuration> _companyInfo;
        Lazy<UpcNote> _newPackage;
        Lazy<UpcNote> _oldPackage;
        Lazy<Client> _lushEssence;

        partial void OnContextCreated()
        {
            _oz = new Lazy<Unit>(() => this.Units.Single(x => x.Name == "oz"));
            _gallon = new Lazy<Unit>(() => this.Units.Single(x => x.Name == "gallon"));
            _color = new Lazy<Unit>(() => this.Units.Single(x => x.Name == "color"));
            _set = new Lazy<Unit>(() => this.Units.Single(x => x.Name == "set"));
            _kit = new Lazy<Unit>(() => this.Units.Single(x => x.Name == "kit"));
            _noUnit = new Lazy<Unit>(() => this.Units.Single(x => x.Name == "none"));

            _companyInfo = new Lazy<Configuration>(() => this.Configuration.Single(x => x.Name.ToLower() == "companyinfo"));
            _newPackage = new Lazy<UpcNote>(() => this.UpcNotes.Single(x => x.Code.ToLower() == "nw"));
            _oldPackage = new Lazy<UpcNote>(() => this.UpcNotes.Single(x => x.Code.ToLower() == "op"));
            _lushEssence = new Lazy<Client>(() => this.Clients.Single(x => x.CompanyName == "Lush Essence")); 
        }

        public Unit Oz { get { return _oz.Value; } }
        public Unit Gallon { get { return _gallon.Value; } }
        public Unit Color { get { return _color.Value; } }
        public Unit Set { get { return _set.Value; } }
        public Unit Kit { get { return _kit.Value; } }
        public Unit NoUnit { get { return _noUnit.Value; } }

        public Configuration CompanyInfo { get { return _companyInfo.Value; } }
        public UpcNote NewPackage { get { return _newPackage.Value; } }
        public UpcNote OldPackage { get { return _oldPackage.Value; } }

        public Client LushEssence { get { return _lushEssence.Value; } }

        [EdmFunction("WarehouseModel", "StringToLong")]
        public static long StringToLong(string value) { throw new NotSupportedException(); }
        [EdmFunction("WarehouseModel", "LongToString")]
        public static string LongToString(long value) { throw new NotSupportedException(); }

        public IQueryable<Product> ProductsByFilter(string filter)
        {
            IQueryable<Product> products = this.Products;
            if (filter == null || filter == "") return products;
            var result = products
                .Where(x =>
                        x.ProductName.Brand.Name.Contains(filter)
                        || x.ProductName.Name.Contains(filter)
                        || x.UPCs.Any(upc => upc.Code.EndsWith(filter))).Include(p => p.ProductName);

            this.Refresh(RefreshMode.StoreWins, result);
            this.Refresh(RefreshMode.StoreWins, result.SelectMany(p => p.UPCs));

            return result;
        }

        public IEnumerable<Tuple<Brand, ClientBrandCommission>> FindClientBrandCommissions(int clientId)
        {
            return
                this.Brands
                .Select(brand => new { brand, pct = brand.ClientBrandCommissions.Where(_ => _.ClientId == clientId).FirstOrDefault() })
                .AsEnumerable()
                .Select(_ => Tuple.Create(_.brand, _.pct));
        }

        public IEnumerable<Tuple<Product, ClientProductCommission>> FindClientProductCommissions(int clientId, int brandId)
        {
            return
                (
                    from product in this.Products
                    where product.ProductName.Brand.Id == brandId
                    select new { product, pct = product.ClientProductCommissions.Where(_ => _.ClientId == clientId).FirstOrDefault() }
                )
                .AsEnumerable()
                .Select(_ => Tuple.Create(_.product, _.pct));
        }

        public IEnumerable<Tuple<Brand, VendorBrandCommission>> FindVendorBrandCommissions(int vendorId)
        {
            return
                this.Brands
                .Select(brand => new { brand, pct = brand.VendorBrandCommissions.Where(_ => _.VendorId == vendorId).FirstOrDefault() })
                .AsEnumerable()
                .Select(_ => Tuple.Create(_.brand, _.pct));
        }

        public IEnumerable<Tuple<Product, VendorProductCommission>> FindVendorProductCommissions(int vendorId, int brandId)
        {
            return
                (
                    from product in this.Products
                    where product.ProductName.Brand.Id == brandId
                    select new { product, pct = product.VendorProductCommissions.Where(_ => _.VendorId == vendorId).FirstOrDefault() }
                )
                .AsEnumerable()
                .Select(_ => Tuple.Create(_.product, _.pct));
        }

        public UPC UpcById(string upc) { return this.UPCs.SingleOrDefault(_ => _.Code == upc); }

        public IQueryable<Invoice> InvoicesSearch(string filter, int? top = null)
        {
            IQueryable<Invoice> result = this.Invoices;
            if (filter != null && filter != "")
                result =
                    from i in result
                    where LongToString(i.Id) == filter
                        || i.Note.Contains(filter)
                        || i.InvoiceLineItems.Any(li => li.UPC.Code.EndsWith(filter))
                        || i.InvoiceLineItems.Any(li => li.UPC.Product.SKU.EndsWith(filter))
                        || i.Client.CompanyName.Contains(filter)
                    select i;
            result = result.OrderByDescending(_ => _.Id);
            if (top.HasValue) result = result.Take(top.Value);

            this.Refresh(RefreshMode.StoreWins, result);
            this.Refresh(RefreshMode.StoreWins, result.SelectMany(invoice => invoice.InvoiceLineItems));

            return result;
        }

        public IQueryable<PurchaseOrder> PurchaseOrdersSearch(string filter, int? top = null)
        {
            IQueryable<PurchaseOrder> result = this.PurchaseOrders;
            if (filter != null && filter != "")
                result =
                    from po in result
                    where LongToString(po.Id) == filter
                        || po.PurchaseOrderLineItems.Any(li => li.UPC.Code.EndsWith(filter))
                        || po.PurchaseOrderLineItems.Any(li => li.UPC.Product.SKU.EndsWith(filter))
                        || po.Vendor.CompanyName.Contains(filter)
                    select po;
            result = result.OrderByDescending(_ => _.Id);
            if (top.HasValue) result = result.Take(top.Value);

            this.Refresh(RefreshMode.StoreWins, result);
            this.Refresh(RefreshMode.StoreWins, result.SelectMany(po => po.PurchaseOrderLineItems));

            return result;
        }

        public string GenerateUpc()
        {
            return this.ExecuteStoreQuery<long>("SELECT dbo.GenerateUpc()").Single().ToString();
        }


        public int AvailableQuantityByUPC(UPC upc)
        {
            return this.ExecuteStoreQuery<int>("SELECT Available FROM dbo.AvailableQuantityByUPC({0})", upc.Id).Single();
        }

        public int ProductOfferAvailableQuantity(Product product)
        {
            return this.ExecuteStoreQuery<int>("SELECT Available FROM dbo.ProductOfferAvailableQuantity({0})", product.Id).Single();
        }

        public bool UpcExists(string code)
        {
            return this.UPCs.Any(upc => upc.Code == code);
        }

        public Product ProductBySkuOrUpc(string code)
        {
            return
            (
                from product in this.Products
                from upc in product.UPCs
                where product.SKU == code || upc.Code == code
                select product
            )
            .Include(product => product.UPCs)
            .FirstOrDefault();
        }

        public IEnumerable<T> GetDeleted<T>(Func<T, bool> filter) where T : EntityObject
        {
            return
                this.ObjectStateManager.GetObjectStateEntries(EntityState.Deleted)
                .Select(x => x.Entity)
                .OfType<T>()
                .Where(filter);
        }
             

        public IEnumerable<InvoiceLineItem> DeletedLineItems(Invoice invoice) 
        {
            return this.GetDeleted<InvoiceLineItem>(li => li.InvoiceId == invoice.Id);
        }

        public IEnumerable<PurchaseOrderLineItem> DeletedLineItems(PurchaseOrder po)
        {
            return this.GetDeleted<PurchaseOrderLineItem>(lineItem => lineItem.PurchaseOrderId == po.Id);
        }

        public void Unchanged(EntityObject entity)
        {
            this.ObjectStateManager.ChangeObjectState(entity, EntityState.Unchanged);
        }

        public UPC UpcById(int id) { return this.UPCs.First(upc => upc.Id == id); }
    }
}

